/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[欢迎加盟知启蒙，一起邂逅框架梦]
 * 
 * https://www.zhiqim.com/gitcan/zhiqim/zhiqim_account.htm
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zhiqim.account.dao;

import java.sql.SQLException;

import org.zhiqim.account.ZacConstants;
import org.zhiqim.account.api.pay.AlipayServer;
import org.zhiqim.account.dbo.user.UserInfo;
import org.zhiqim.account.dbo.user.UserTrade;
import org.zhiqim.account.dbo.user.UserTradeAlipay;
import org.zhiqim.account.dbo.user.UserTradeAlipayRefund;
import org.zhiqim.account.dbo.user.UserTradeWxpay;
import org.zhiqim.kernel.Global;
import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.transaction.Transaction;
import org.zhiqim.kernel.transaction.TransactionManager;
import org.zhiqim.kernel.util.Ids;
import org.zhiqim.kernel.util.Sqls;
import org.zhiqim.orm.ORMException;
import org.zhiqim.orm.ZTable;
import org.zhiqim.orm.dbo.Selector;
import org.zhiqim.orm.dbo.Updater;

import com.alipay.api.AlipayConstants;
import com.alipay.api.response.AlipayTradeRefundResponse;

/**
 * 用户交易数据访问对象
 *
 * @version v1.0.0 @author zouzhigang 2018-3-26 新建与整理
 */
@AnAlias("UserTradeDao")
public class UserTradeDao implements AlipayConstants, ZacConstants
{
    private static final Log log = LogFactory.getLog(UserTradeDao.class);
    
    /**
     * 用户充值，TODO 注意，当前未作原子操作处理，后续优化
     * 
     * @param alipay        支付宝交易对象
     * @return              用户对象
     * @throws SQLException 可能的SQL异常
     * @throws ORMException 可能的映射异常
     */
    public static UserInfo doCharge(UserTradeAlipay alipay) throws Exception
    {
        //1.先查余额
        UserInfo user = Global.get(ZTable.class).item(UserInfo.class, new Selector("userNick,userBalance", "userId", alipay.getUserId()));
        Transaction tx = null;
        
        try
        {
            tx = TransactionManager.beginTransaction();
            
            //2.插入明细
            UserTrade trade = new UserTrade();
            trade.setUserId(alipay.getUserId());
            trade.setTradeId(alipay.getTradeId());
            trade.setTradeAmount(alipay.getTradeAmount());
            trade.setTradeTime(alipay.getTradeNotified()==null?Sqls.nowTimestamp():alipay.getTradeNotified());
            trade.setTradeType(CHARGE);
            trade.setTradeParty("支付宝");
            trade.setTradePartyNo(alipay.getTradeNo());
            trade.setTradePartySubject(alipay.getTradeSubject());
            trade.setTradePartyAccount(alipay.getTradeAccount());
            trade.setTradeData(0);
            trade.setUserBalance(user.getUserBalance()+alipay.getTradeAmount());
            
            Global.get(ZTable.class).insert(trade);
            
            //3.更新余额
            Updater updater = new Updater();
            updater.addMust("userId", alipay.getUserId());
            updater.addFieldExpression("userBalance", "userBalance+"+alipay.getTradeAmount()); 
            Global.get(ZTable.class).update(UserInfo.class, updater);
            
            tx.commit();
        }
        catch (Exception e) 
        {
            tx.rollback();
            throw e;
        }
        finally
        {
            tx.close();
        }
        
        return user;
    }
    
    /**
     * 用户充值，TODO 注意，当前未作原子操作处理，后续优化
     * 
     * @param wxpay     微信交易对象
     * @return              用户对象
     * @throws SQLException 可能的SQL异常
     * @throws ORMException 可能的映射异常
     */
    public static UserInfo doCharge(UserTradeWxpay wxpay) throws Exception
    {
        //1.先查余额
        UserInfo user = Global.get(ZTable.class).item(UserInfo.class, new Selector("userNick,userBalance", "userId", wxpay.getUserId()));
        Transaction tx = null;
        
        try
        {
            tx = TransactionManager.beginTransaction();
            
            //2.插入明细
            UserTrade trade = new UserTrade();
            trade.setUserId(wxpay.getUserId());
            trade.setTradeId(wxpay.getTradeId());
            trade.setTradeAmount(wxpay.getTradeAmount());
            trade.setTradeTime(wxpay.getTradeCreated());
            trade.setTradeType(CHARGE);
            trade.setTradeParty("微信");
            trade.setTradePartyNo(wxpay.getTradeNo());
            trade.setTradePartySubject(wxpay.getTradeSubject());
            trade.setTradePartyAccount(user.getUserNick());
            trade.setTradeData(0);
            trade.setUserBalance(user.getUserBalance()+wxpay.getTradeAmount());
            
            Global.get(ZTable.class).insert(trade);
            
            //3.更新余额
            Updater updater = new Updater();
            updater.addMust("userId", wxpay.getUserId());
            updater.addFieldExpression("userBalance", "userBalance+"+wxpay.getTradeAmount()); 
            Global.get(ZTable.class).update(UserInfo.class, updater);
            
            tx.commit();
        }
        catch (Exception e) 
        {
            tx.rollback();
            throw e;
        }
        finally
        {
            tx.close();
        }
        
        return user;
    }
    
    /**
     * 用户余额消费
     * 
     * @param balancePay    余额交易对象
     * @return              用户对象
     * @throws SQLException 可能的SQL异常
     * @throws ORMException 可能的映射异常
     */
    public static UserInfo doExpend(UserTrade balancePay) throws Exception
    {
        //1.先查余额
        UserInfo user = Global.get(ZTable.class).item(UserInfo.class, new Selector("userNick,userBalance", "userId", balancePay.getUserId()));
        Transaction tx = null;
        
        try
        {
            tx = TransactionManager.beginTransaction();
            
            //2.插入明细
            UserTrade trade = new UserTrade();
            trade.setUserId(balancePay.getUserId());
            trade.setTradeId(balancePay.getTradeId());
            trade.setTradeTime(balancePay.getTradeTime());
            trade.setTradeType(CONSUM);
            trade.setTradeParty("余额");
            trade.setTradePartyNo(balancePay.getTradePartyNo());
            trade.setTradePartySubject(balancePay.getTradePartySubject());
            trade.setTradePartyAccount(user.getUserNick());
            trade.setTradeAmount(balancePay.getTradeAmount());
            trade.setTradeData(0);
            trade.setUserBalance(user.getUserBalance()-balancePay.getTradeAmount());
            
            Global.get(ZTable.class).insert(trade);
            
            //3.更新余额
            Updater updater = new Updater();
            updater.addMust("userId", balancePay.getUserId());
            updater.addFieldExpression("userBalance", "userBalance-"+balancePay.getTradeAmount()); 
            Global.get(ZTable.class).update(UserInfo.class, updater);
            
            tx.commit();
        }
        catch (Exception e) 
        {
            tx.rollback();
            throw e;
        }
        finally
        {
            tx.close();
        }
        
        return user;
    }
    
    /**
     * 用户退款，TODO 注意，异常情况还需要后续优化
     * 
     * @param trade         原交易
     * @param refund        退款详情
     * @param user          用户账户
     * @throws SQLException 可能的SQL异常
     * @throws ORMException 可能的映射异常
     */
    public static String doRefund(UserTrade trade, UserInfo user, int refundAmount)  throws Exception
    {
        long refundTradeId = Ids.longId(REF_SEQ);
        String refundReason = "买家请求退款";
        String refundStatus = REFUND_WAITING;
        String refundError = null;
        
        //1.先插入支付宝退款交易
        UserTradeAlipayRefund refund = new UserTradeAlipayRefund();
        refund.setUserId(trade.getUserId());
        refund.setTradeId(refundTradeId);
        refund.setTradeOrigId(trade.getTradeId());
        refund.setTradeNo(trade.getTradePartyNo());
        refund.setTradeAmount(trade.getTradeAmount());
        refund.setRefundStatus(refundStatus);
        refund.setRefundAmount(refundAmount);
        refund.setRefundReason(refundReason);
        Global.get(ZTable.class).insert(refund);
        
        //2.事务处理，保障退款正确性
        Transaction tx = null;

        try
        {
            tx = TransactionManager.beginTransaction();
            
            //2.1 更新原充值交易为已退款金额
            Global.get(ZTable.class).update(UserTrade.class, new Updater().addMust("tradeId", trade.getTradeId()).addFieldExpression("tradeData", "tradeData+"+refundAmount));
            
            //2.2 更新用户余额
            Global.get(ZTable.class).update(UserInfo.class, new Updater().addMust("userId", trade.getUserId()).addFieldExpression("userBalance", "userBalance-"+refundAmount));
            
            //2.3 插入新退款交易
            UserTrade tradeLog = new UserTrade();
            tradeLog.setUserId(trade.getUserId());
            tradeLog.setTradeId(refund.getTradeId());
            tradeLog.setTradeAmount(refund.getRefundAmount());
            tradeLog.setTradeTime(Sqls.nowTimestamp());
            tradeLog.setTradeType(REFUND);
            tradeLog.setTradeParty("支付宝");
            tradeLog.setTradePartyAccount(trade.getTradePartyAccount());
            tradeLog.setTradePartyNo(refund.getTradeNo());
            tradeLog.setTradeData(trade.getTradeId());
            tradeLog.setUserBalance(user.getUserBalance() - refund.getRefundAmount());
            Global.get(ZTable.class).insert(tradeLog);
            
            //2.4 执行退款操作
            AlipayTradeRefundResponse response = Global.getService(AlipayServer.class).tradeRefund(trade.getTradeId(), trade.getTradePartyNo(), refundTradeId, refundAmount, refundReason, null);
            if (!response.isSuccess())
            {//失败回滚
                tx.rollback();
                refundStatus = REFUND_FAILURE;
                refundError = "退款失败："+response.getMsg();
            }
            else
            {//成功提交
                tx.commit();
                refundStatus = REFUND_SUCCESS;
            }
        }
        catch (Exception e) 
        {
            tx.rollback();
            refundStatus = REFUND_EXCEPTN;
            refundError = "退款异常：" + e.getMessage();
        }
        finally
        {
            tx.close();
        }
        
        //3.最后更新退款状态
        try
        {
            Updater updater = new Updater()
                .addMust("tradeId", refundTradeId)
                .addField("refundStatus", refundStatus);
            Global.get(ZTable.class).update(UserTradeAlipayRefund.class, updater);
        }
        catch (Exception e)
        {//TODO 这里异常后续也进入异常列表，通过后台任务再次检查，但不能抛出给业务
            log.error("更新支付宝退款状态异常", e);
        }
        
        return refundError;
    }

}
